/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.debug.codecoverage;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.FontUtils;
import org.python.pydev.core.IFontUsage;
import org.python.pydev.core.callbacks.ICallbackListener;
import org.python.pydev.core.callbacks.ICallbackWithListeners;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.tooltips.presenter.StyleRangeWithCustomData;
import org.python.pydev.debug.ui.launching.PythonRunnerCallbacks;
import org.python.pydev.debug.ui.launching.PythonRunnerCallbacks.CreatedCommandLineParams;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.editor.actions.PyAction;
import org.python.pydev.editorinput.PyOpenEditor;
import org.python.pydev.editorinput.PySourceLocatorBase;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.tree.AllowValidPathsFilter;
import org.python.pydev.tree.FileTreeLabelProvider;
import org.python.pydev.tree.FileTreePyFilesProvider;
import org.python.pydev.ui.IViewCreatedObserver;
import org.python.pydev.ui.IViewWithControls;
import org.python.pydev.ui.ViewPartWithOrientation;
import org.python.pydev.utils.ProgressAction;
import org.python.pydev.utils.ProgressOperation;
import org.python.pydev.utils.PyFilteredTree;
import com.aptana.shared_core.io.FileUtils;
import com.aptana.shared_core.structure.Tuple;
import com.aptana.shared_core.utils.RunInUiThread;
/**
* This sample class demonstrates how to plug-in a new workbench view. The view shows data obtained from the model. The sample creates a
* dummy model on the fly, but a real implementation would connect to the model available either in this or another plug-in (e.g. the
* workspace). The view is connected to the model using a content provider.
* <p>
* The view uses a label provider to define how model objects should be presented in the view. Each view can present the same model objects
* using different labels and icons, if needed. Alternatively, a single label provider can be shared between views in order to ensure that
* objects of the same type are presented in the same way everywhere.
* <p>
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class PyCodeCoverageView extends ViewPartWithOrientation implements IViewWithControls {
public static final String PYCOVERAGE_VIEW_ORIENTATION = "PYCOVERAGE_VIEW_ORIENTATION";
@Override
public String getOrientationPreferencesKey() {
return PYCOVERAGE_VIEW_ORIENTATION;
}
public static String PY_COVERAGE_VIEW_ID = "org.python.pydev.views.PyCodeCoverageView";
//layout stuff
private Composite leftComposite;
//actions
/**
* double click the tree
*/
private DoubleClickTreeAction doubleClickAction = new DoubleClickTreeAction();
/**
* changed selected element
*/
private SelectionChangedTreeAction selectionChangedAction = new SelectionChangedTreeAction();
/**
* choose new dir
*/
private ProgressAction chooseAction = new ChooseAction();
/**
* Opens the coverage folder action
*/
protected Action openCoverageFolderAction = new OpenCoverageFolderAction();
/**
* clear the results (and erase .coverage file)
*/
protected ProgressAction clearAction = new ClearAction();
protected Action selectColumnsAction = new SelectColumnsAction();
/**
* get the new results from the .coverage file
*/
protected RefreshAction refreshAction = new RefreshAction();
private Button chooseButton;
//write the results here
private StyledText text;
//tree so that user can browse results.
private TreeViewer viewer;
public TreeViewer getTreeViewer() {
return viewer;
}
private SashForm sash;
/*default for testing */Button allRunsGoThroughCoverage;
/*default for testing */Button clearCoverageInfoOnNextLaunch;
/*default for testing */Button refreshCoverageInfoOnNextLaunch;
private Label labelErrorFolderNotSelected;
//Actions ------------------------------
/**
* In this action we have to go and refresh all the info based on the chosen dir.
*
* @author Fabio Zadrozny
*/
private final class OpenCoverageFolderAction extends Action {
public OpenCoverageFolderAction() {
this.setText("Open folder with .coverage files.");
}
public void run() {
try {
FileUtils.openDirectory(PyCoverage.getCoverageDirLocation());
} catch (Exception e) {
Log.log(e);
}
}
}
/**
* In this action we have to go and refresh all the info based on the chosen dir.
*
* @author Fabio Zadrozny
*/
private final class RefreshAction extends ProgressAction {
public RefreshAction() {
this.setText("Refresh coverage information");
}
public void run() {
try {
executeRefreshAction(this.monitor);
} catch (Exception e) {
Log.log(e);
}
}
}
/**
* Note that this method should never be directly called.
*
* For a proper refresh do:
* ProgressOperation.startAction(getSite().getShell(), action, true);
*/
/*default for tests*/void executeRefreshAction(IProgressMonitor monitor) {
if (viewer == null) { //Safeguard: if the view containing this one was removed and for some reason not properly disposed, this would occur.
return;
}
if (monitor == null) {
monitor = new NullProgressMonitor();
}
IContainer lastChosenDir = PyCoveragePreferences.getLastChosenDir();
if (lastChosenDir == null) {
return;
}
PyCoverage.getPyCoverage().refreshCoverageInfo(lastChosenDir, monitor);
File input = lastChosenDir.getLocation().toFile();
viewer.refresh();
ITreeContentProvider contentProvider = (ITreeContentProvider) viewer.getContentProvider();
ISelection selection = viewer.getSelection();
if (selection instanceof StructuredSelection) {
StructuredSelection current = (StructuredSelection) selection;
Object firstElement = current.getFirstElement();
if (firstElement != null) {
onSelectedFileInTree(firstElement);
return;
}
}
//If the current selection wasn't valid, select something or notify that nothing is selected.
Object[] children = contentProvider.getChildren(input);
if (children.length > 0) {
viewer.setSelection(new StructuredSelection(children[0]));
} else {
onSelectedFileInTree(null);
}
}
private final ICallbackListener<Process> afterCreatedProcessListener = new ICallbackListener<Process>() {
public Object call(final Process obj) {
if (viewer == null) { //Safeguard: if the view containing this one was removed and for some reason not properly disposed, this would occur.
return null;
}
new Thread() {
@Override
public void run() {
boolean finished = false;
while (!finished) {
try {
obj.waitFor();
finished = true;
} catch (InterruptedException e) {
//ignore
}
}
//If it got here, the process was finished (so, check the setting on refresh and do it if
//needed).
if (PyCoveragePreferences.getRefreshAfterNextLaunch()) {
RunInUiThread.async(new Runnable() {
public void run() {
ProgressOperation.startAction(getSite().getShell(), refreshAction, true);
}
});
}
}
}.start();
return null;
}
};
private final ICallbackListener<PythonRunnerCallbacks.CreatedCommandLineParams> onCreatedCommandLineListener = new ICallbackListener<PythonRunnerCallbacks.CreatedCommandLineParams>() {
public Object call(CreatedCommandLineParams arg) {
if (viewer == null) { //Safeguard: if the view containing this one was removed and for some reason not properly disposed, this would occur.
return null;
}
if (arg.coverageRun) {
if (PyCoveragePreferences.getClearCoverageInfoOnNextLaunch()) {
try {
PyCoverage.getPyCoverage().clearInfo();
} catch (Exception e) {
Log.log(e);
}
}
}
return null;
}
};
public static IContainer getChosenDir() {
return PyCoveragePreferences.getLastChosenDir();
}
/**
*
* @author Fabio Zadrozny
*/
private final class ClearAction extends ProgressAction {
public ClearAction() {
this.setText("Clear coverage information");
}
public void run() {
PyCoverage.getPyCoverage().clearInfo();
MessageDialog.openInformation(getSite().getShell(), "Cleared", "All the coverage data has been cleared!");
text.setText("Data cleared (NOT REFRESHED).");
}
}
/**
*
* @author Fabio Zadrozny
*/
private final class SelectColumnsAction extends Action {
public SelectColumnsAction() {
this.setText("Select the number of columns for the name.");
}
public void run() {
InputDialog d = new InputDialog(PyAction.getShell(), "Enter number of columns",
"Enter the number of columns to be used for the name.", ""
+ PyCoveragePreferences.getNameNumberOfColumns(), new IInputValidator() {
public String isValid(String newText) {
if (newText.trim().length() == 0) {
return "Please enter a number > 5";
}
try {
int i = Integer.parseInt(newText);
if (i < 6) {
return "Please enter a number > 5";
}
if (i > 256) {
return "Please enter a number <= 256";
}
} catch (NumberFormatException e) {
return "Please enter a number > 5";
}
return null;
}
});
int retCode = d.open();
if (retCode == InputDialog.OK) {
PyCoveragePreferences.setNameNumberOfColumns(Integer.parseInt(d.getValue()));
onSelectedFileInTree(lastSelectedFile);
}
}
}
/**
*
* @author Fabio Zadrozny
*/
private final class SelectionChangedTreeAction extends Action {
public void run() {
run((IStructuredSelection) viewer.getSelection());
}
/**
* @param event
*/
public void runWithEvent(SelectionChangedEvent event) {
run((IStructuredSelection) event.getSelection());
}
public void run(IStructuredSelection selection) {
Object obj = selection.getFirstElement();
if (obj == null)
return;
onSelectedFileInTree(obj);
}
}
private File lastSelectedFile;
private void onSelectedFileInTree(Object obj) {
if (obj == null) {
text.setText("");
} else {
File realFile = new File(obj.toString());
if (realFile.exists()) {
lastSelectedFile = realFile;
Tuple<String, List<StyleRange>> statistics = PyCoverage.getPyCoverage().cache.getStatistics(
realFile.toString(), realFile);
text.setText(statistics.o1);
text.setStyleRanges(statistics.o2.toArray(new StyleRange[statistics.o2.size()]));
} else {
text.setText("Selection no longer exists in disk: " + obj.toString());
}
}
}
/**
*
* @author Fabio Zadrozny
*/
private final class DoubleClickTreeAction extends ProgressAction {
public void run() {
run(viewer.getSelection());
}
/**
* @param event
*/
public void runWithEvent(DoubleClickEvent event) {
run(event.getSelection());
}
public void run(ISelection selection) {
try {
Object obj = ((IStructuredSelection) selection).getFirstElement();
File realFile = new File(obj.toString());
if (realFile.exists() && !realFile.isDirectory()) {
openFileWithCoverageMarkers(realFile);
}
} catch (Exception e) {
Log.log(e);
}
}
}
/**
*
* @author Fabio Zadrozny
*/
private final class ChooseAction extends ProgressAction {
public void run() {
ContainerSelectionDialog dialog = new ContainerSelectionDialog(getSite().getShell(), null, false,
"Choose folder to be analyzed in the code-coverage");
dialog.showClosedProjects(false);
if (dialog.open() != Window.OK) {
return;
}
Object[] objects = dialog.getResult();
if (objects.length == 1) { //only one folder can be selected
if (objects[0] instanceof IPath) {
IPath p = (IPath) objects[0];
IWorkspace w = ResourcesPlugin.getWorkspace();
IContainer folderForLocation = (IContainer) w.getRoot().findMember(p);
setSelectedContainer(folderForLocation);
}
}
}
}
public void setSelectedContainer(IContainer container) {
lastSelectedFile = null;
PyCoveragePreferences.setLastChosenDir(container);
updateErrorMessages();
File input = container.getLocation().toFile();
viewer.setInput(input);
ITreeContentProvider contentProvider = (ITreeContentProvider) viewer.getContentProvider();
Object[] children = contentProvider.getChildren(input);
if (children.length > 0) {
viewer.setSelection(new StructuredSelection(children[0]));
} else {
viewer.setSelection(new StructuredSelection());
}
ProgressOperation.startAction(getSite().getShell(), refreshAction, true);
}
// Class -------------------------------------------------------------------
/**
* The constructor.
*/
public PyCodeCoverageView() {
List<IViewCreatedObserver> participants = ExtensionHelper
.getParticipants(ExtensionHelper.PYDEV_VIEW_CREATED_OBSERVER);
for (IViewCreatedObserver iViewCreatedObserver : participants) {
iViewCreatedObserver.notifyViewCreated(this);
}
}
public void refresh() {
viewer.refresh();
getSite().getPage().bringToTop(this);
}
@Override
protected void setNewOrientation(int orientation) {
if (sash != null && !sash.isDisposed() && fParent != null && !fParent.isDisposed()) {
GridLayout layout = (GridLayout) fParent.getLayout();
if (orientation == VIEW_ORIENTATION_HORIZONTAL) {
sash.setOrientation(SWT.HORIZONTAL);
layout.numColumns = 2;
} else {
sash.setOrientation(SWT.VERTICAL);
layout.numColumns = 1;
}
fParent.layout();
}
}
/**
* This is a callback that will allow us to create the viewer and initialize it.
*/
public void createPartControl(Composite parent) {
super.createPartControl(parent);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.verticalSpacing = 2;
layout.marginWidth = 0;
layout.marginHeight = 2;
parent.setLayout(layout);
sash = new SashForm(parent, SWT.HORIZONTAL);
GridData layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.grabExcessVerticalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
layoutData.verticalAlignment = GridData.FILL;
sash.setLayoutData(layoutData);
parent = sash;
leftComposite = new Composite(parent, SWT.MULTI);
layout = new GridLayout();
layout.numColumns = 2;
layout.verticalSpacing = 2;
layout.marginWidth = 0;
layout.marginHeight = 2;
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.grabExcessVerticalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
layoutData.verticalAlignment = GridData.FILL;
leftComposite.setLayoutData(layoutData);
leftComposite.setLayout(layout);
text = new StyledText(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
onControlCreated.call(text);
try {
text.setFont(new Font(null, FontUtils.getFontData(IFontUsage.WIDGET, false)));
} catch (Exception e) {
//ok, might mot be available.
}
text.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
int offset;
try {
offset = text.getOffsetAtLocation(new Point(e.x, e.y));
} catch (IllegalArgumentException e1) {
return; //Yes, in this case we clicked out of the possible range (i.e.: if we had no contents).
}
StyleRange r = text.getStyleRangeAtOffset(offset);
if (r instanceof StyleRangeWithCustomData) {
StyleRangeWithCustomData styleRangeWithCustomData = (StyleRangeWithCustomData) r;
Object o = styleRangeWithCustomData.customData;
if (o instanceof FileNode) {
FileNode fileNode = (FileNode) o;
if (fileNode.node != null && fileNode.node.exists()) {
openFileWithCoverageMarkers(fileNode.node);
}
}
}
}
});
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.grabExcessVerticalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
layoutData.verticalAlignment = GridData.FILL;
text.setLayoutData(layoutData);
parent = leftComposite;
//all the runs from now on go through coverage?
allRunsGoThroughCoverage = new Button(parent, SWT.CHECK);
allRunsGoThroughCoverage.setText("Enable code coverage for new launches?");
allRunsGoThroughCoverage.setSelection(PyCoveragePreferences.getInternalAllRunsDoCoverage());
allRunsGoThroughCoverage.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
PyCoveragePreferences.setInternalAllRunsDoCoverage(allRunsGoThroughCoverage.getSelection());
updateErrorMessages();
}
});
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
layoutData.horizontalSpan = 2;
allRunsGoThroughCoverage.setLayoutData(layoutData);
//end all runs go through coverage
//Clear the coverage info on each launch?
clearCoverageInfoOnNextLaunch = new Button(parent, SWT.CHECK);
clearCoverageInfoOnNextLaunch.setText("Auto clear on a new launch?");
clearCoverageInfoOnNextLaunch.setSelection(PyCoveragePreferences.getClearCoverageInfoOnNextLaunch());
clearCoverageInfoOnNextLaunch.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
PyCoveragePreferences.setClearCoverageInfoOnNextLaunch(clearCoverageInfoOnNextLaunch.getSelection());
}
});
PythonRunnerCallbacks.onCreatedCommandLine.registerListener(onCreatedCommandLineListener);
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
clearCoverageInfoOnNextLaunch.setLayoutData(layoutData);
Button button = new Button(parent, SWT.PUSH);
button.setText("Clear");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ProgressOperation.startAction(getSite().getShell(), clearAction, true);
}
});
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = false;
layoutData.widthHint = 50;
layoutData.horizontalAlignment = GridData.END;
button.setLayoutData(layoutData);
//end all runs go through coverage
//Refresh the coverage info on each launch?
refreshCoverageInfoOnNextLaunch = new Button(parent, SWT.CHECK);
refreshCoverageInfoOnNextLaunch.setText("Auto refresh on new launch?");
refreshCoverageInfoOnNextLaunch.setSelection(PyCoveragePreferences.getRefreshAfterNextLaunch());
refreshCoverageInfoOnNextLaunch.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
PyCoveragePreferences.setRefreshAfterNextLaunch(refreshCoverageInfoOnNextLaunch.getSelection());
}
});
PythonRunnerCallbacks.afterCreatedProcess.registerListener(afterCreatedProcessListener);
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
refreshCoverageInfoOnNextLaunch.setLayoutData(layoutData);
button = new Button(parent, SWT.PUSH);
button.setText("Refresh");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ProgressOperation.startAction(getSite().getShell(), refreshAction, true);
}
});
layoutData = new GridData();
layoutData.widthHint = 50;
layoutData.grabExcessHorizontalSpace = true;
layoutData.horizontalAlignment = GridData.END;
button.setLayoutData(layoutData);
//end refresh
//choose button
chooseButton = new Button(parent, SWT.PUSH);
createButton(parent, chooseButton, "Choose folder to analyze", chooseAction);
//end choose button
PatternFilter patternFilter = new PatternFilter();
FilteredTree filter = PyFilteredTree.create(parent, patternFilter, true);
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.grabExcessVerticalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
layoutData.verticalAlignment = GridData.FILL;
layoutData.horizontalSpan = 2;
filter.setLayoutData(layoutData);
viewer = filter.getViewer();
onControlCreated.call(viewer);
viewer.setContentProvider(new FileTreePyFilesProvider());
viewer.setLabelProvider(new FileTreeLabelProvider());
viewer.addFilter(new AllowValidPathsFilter());
hookViewerActions();
Tree tree = (Tree) viewer.getControl();
TreeItem item = new TreeItem(tree, SWT.NONE);
item.setText("Altenatively, to select a folder, drag it to this area.");
item = new TreeItem(tree, SWT.NONE);
item = new TreeItem(tree, SWT.NONE);
item.setText("Note: Only the sources under the folder selected");
item = new TreeItem(tree, SWT.NONE);
item.setText("will have coverage information collected.");
// Allow data to be copied or moved to the drop target
int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
DropTarget target = new DropTarget(tree, operations);
// Receive data in Text or File format
final FileTransfer fileTransfer = FileTransfer.getInstance();
Transfer[] types = new Transfer[] { fileTransfer };
target.setTransfer(types);
target.addDropListener(new DropTargetListener() {
public void dragEnter(DropTargetEvent event) {
if (event.detail == DND.DROP_DEFAULT) {
if ((event.operations & DND.DROP_COPY) != 0) {
event.detail = DND.DROP_COPY;
} else {
event.detail = DND.DROP_NONE;
}
}
// will accept text but prefer to have files dropped
for (int i = 0; i < event.dataTypes.length; i++) {
if (fileTransfer.isSupportedType(event.dataTypes[i])) {
event.currentDataType = event.dataTypes[i];
// files should only be copied
if (event.detail != DND.DROP_COPY) {
event.detail = DND.DROP_NONE;
}
break;
}
}
}
public void dragOver(DropTargetEvent event) {
}
public void dragOperationChanged(DropTargetEvent event) {
if (event.detail == DND.DROP_DEFAULT) {
if ((event.operations & DND.DROP_COPY) != 0) {
event.detail = DND.DROP_COPY;
} else {
event.detail = DND.DROP_NONE;
}
}
// allow text to be moved but files should only be copied
if (fileTransfer.isSupportedType(event.currentDataType)) {
if (event.detail != DND.DROP_COPY) {
event.detail = DND.DROP_NONE;
}
}
}
public void dragLeave(DropTargetEvent event) {
}
public void dropAccept(DropTargetEvent event) {
}
public void drop(DropTargetEvent event) {
if (fileTransfer.isSupportedType(event.currentDataType)) {
String[] files = (String[]) event.data;
if (files.length == 1) {
File file = new File(files[0]);
if (file.isDirectory()) {
PySourceLocatorBase locator = new PySourceLocatorBase();
IContainer container = locator.getWorkspaceContainer(file);
if (container != null && container.exists()) {
setSelectedContainer(container);
}
}
}
}
}
});
configureToolbar();
updateErrorMessages();
}
private void configureToolbar() {
IActionBars actionBars = getViewSite().getActionBars();
//IToolBarManager toolbarManager = actionBars.getToolBarManager();
IMenuManager menuManager = actionBars.getMenuManager();
menuManager.add(selectColumnsAction);
//menuManager.add(clearAction);
//menuManager.add(refreshAction);
if (FileUtils.getSupportsOpenDirectory()) {
menuManager.add(openCoverageFolderAction);
}
addOrientationPreferences(menuManager);
}
/**
* Create button with hooked action.
*
* @param parent
* @param button
* @param string
*/
private void createButton(Composite parent, Button button, String txt, final ProgressAction action) {
GridData layoutData;
button.setText(txt);
button.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
ProgressOperation.startAction(getSite().getShell(), action, true);
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.horizontalAlignment = GridData.FILL;
layoutData.horizontalSpan = 2;
button.setLayoutData(layoutData);
}
/**
*
* Add the double click and selection changed action
*/
private void hookViewerActions() {
viewer.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
doubleClickAction.runWithEvent(event);
}
});
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
selectionChangedAction.runWithEvent(event);
}
});
}
/**
* Passing the focus request to the viewer's control.
*/
public void setFocus() {
viewer.getControl().setFocus();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#dispose()
*/
@Override
public void dispose() {
try {
PythonRunnerCallbacks.afterCreatedProcess.unregisterListener(afterCreatedProcessListener);
PythonRunnerCallbacks.onCreatedCommandLine.unregisterListener(onCreatedCommandLineListener);
PyCoveragePreferences.setInternalAllRunsDoCoverage(false);
PyCoveragePreferences.setLastChosenDir(null);
if (text != null) {
onControlDisposed.call(text);
text.dispose();
text = null;
}
if (viewer != null) {
onControlDisposed.call(viewer);
viewer.getTree().dispose();
viewer = null;
}
} catch (Throwable e) {
Log.log(e);
}
super.dispose();
}
private void updateErrorMessages() {
boolean showError = false;
if (PyCoveragePreferences.getInternalAllRunsDoCoverage()) {
if (PyCoveragePreferences.getLastChosenDir() == null) {
showError = true;
}
}
if (showError) {
if (labelErrorFolderNotSelected == null) {
labelErrorFolderNotSelected = new Label(leftComposite, SWT.NONE);
labelErrorFolderNotSelected.setForeground(PydevPlugin.getColorCache().getColor("RED"));
labelErrorFolderNotSelected.setText("Folder must be selected for launching with coverage.");
GridData layoutData = new GridData();
layoutData.grabExcessHorizontalSpace = true;
layoutData.horizontalSpan = 2;
layoutData.horizontalAlignment = GridData.FILL;
labelErrorFolderNotSelected.setLayoutData(layoutData);
}
} else {
if (labelErrorFolderNotSelected != null) {
this.labelErrorFolderNotSelected.dispose();
this.labelErrorFolderNotSelected = null;
}
}
this.leftComposite.layout();
}
/**
* Gets the py code coverage view. May only be called in the UI thread. If the view is not visible, if createIfNotThere
* is true, it's made visible.
*
* Note that it may return null if createIfNotThere == false and the view is not currently shown or if not in the
* UI thread.
*/
public static PyCodeCoverageView getView(boolean createIfNotThere) {
IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
try {
if (workbenchWindow == null) {
return null;
}
IWorkbenchPage page = workbenchWindow.getActivePage();
if (createIfNotThere) {
return (PyCodeCoverageView) page.showView(PY_COVERAGE_VIEW_ID, null, IWorkbenchPage.VIEW_ACTIVATE);
} else {
IViewReference viewReference = page.findViewReference(PY_COVERAGE_VIEW_ID);
if (viewReference != null) {
//if it's there, return it (but don't restore it if it's still not there).
//when made visible, it'll handle things properly later on.
return (PyCodeCoverageView) viewReference.getView(false);
}
}
} catch (Exception e) {
Log.log(e);
}
return null;
}
public String getCoverageText() {
return this.text.getText();
}
/**
* this is the type of the marker
*/
public static final String PYDEV_COVERAGE_MARKER = "org.python.pydev.debug.pydev_coverage_marker";
private void openFileWithCoverageMarkers(File realFile) {
IEditorPart editor = PyOpenEditor.doOpenEditor(realFile);
if (editor instanceof PyEdit) {
PyEdit e = (PyEdit) editor;
IEditorInput input = e.getEditorInput();
final IFile original = (input instanceof IFileEditorInput) ? ((IFileEditorInput) input).getFile() : null;
if (original == null)
return;
final IDocument document = e.getDocumentProvider().getDocument(e.getEditorInput());
//When creating it, it'll already start to listen for changes to remove the marker when needed.
new RemoveCoverageMarkersListener(document, e, original);
final FileNode cache = (FileNode) PyCoverage.getPyCoverage().cache.getFile(realFile);
if (cache != null) {
IWorkspaceRunnable r = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
final String type = PYDEV_COVERAGE_MARKER;
try {
original.deleteMarkers(type, false, 1);
} catch (CoreException e1) {
Log.log(e1);
}
final String message = "Not Executed";
for (Iterator<Tuple<Integer, Integer>> it = cache.notExecutedIterator(); it.hasNext();) {
try {
Map<String, Object> map = new HashMap<String, Object>();
Tuple<Integer, Integer> startEnd = it.next();
IRegion region = document.getLineInformation(startEnd.o1 - 1);
int errorStart = region.getOffset();
region = document.getLineInformation(startEnd.o2 - 1);
int errorEnd = region.getOffset() + region.getLength();
map.put(IMarker.MESSAGE, message);
map.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
map.put(IMarker.CHAR_START, errorStart);
map.put(IMarker.CHAR_END, errorEnd);
map.put(IMarker.TRANSIENT, Boolean.valueOf(true));
map.put(IMarker.PRIORITY, new Integer(IMarker.PRIORITY_HIGH));
MarkerUtilities.createMarker(original, map, type);
} catch (Exception e1) {
Log.log(e1);
}
}
}
};
try {
original.getWorkspace().run(r, null, IWorkspace.AVOID_UPDATE, null);
} catch (CoreException e1) {
Log.log(e1);
}
}
}
}
public ICallbackWithListeners getOnControlCreated() {
return onControlCreated;
}
public ICallbackWithListeners getOnControlDisposed() {
return onControlDisposed;
}
}